/////////////////////////////////////////////////////////////////////////////
// Name:        tts_htmlparser.cpp
// Purpose:     Simple HTML parser
// Author:      Julian Smart
// Modified by:
// Created:     2002-09-25
// RCS-ID:      $Id: htmlparser.h,v 1.2 2006/12/14 04:19:24 anthemion Exp $
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#ifndef _TTS_HTMLPARSER_H_
#define _TTS_HTMLPARSER_H_

#include "wx/module.h"
#include "wx/stream.h"

/*
 * wxTTSSimpleHtmlAttribute
 * Representation of an attribute
 */

class wxTTSSimpleHtmlAttribute
{
    friend class wxTTSSimpleHtmlTag;
public:
    wxTTSSimpleHtmlAttribute(const wxString& name, const wxString& value)
    {
        m_name = name; m_value = value; m_next = NULL;
    }
//// Operations

    // Write this attribute
    void Write(wxOutputStream& stream);

//// Accessors
    const wxString& GetName() const { return m_name; }
    const wxString& GetValue() const { return m_value; }

    wxTTSSimpleHtmlAttribute* GetNextAttribute() { return m_next; }
    void SetNextAttribute(wxTTSSimpleHtmlAttribute* attr) { m_next = attr; }

    bool HasName(const wxString& name) const { return (0 == m_name.CmpNoCase(name)); }
    bool HasValue(const wxString& val) const { return (0 == m_value.CmpNoCase(val)); }

private:
    wxString                m_name;
    wxString                m_value;
    wxTTSSimpleHtmlAttribute*  m_next;
};


/*
 * wxTTSSimpleHtmlTag
 * Representation of a tag or chunk of text
 */

enum { wxTTSSimpleHtmlTag_Text, wxTTSSimpleHtmlTag_TopLevel, wxTTSSimpleHtmlTag_Open, wxTTSSimpleHtmlTag_Close, wxTTSSimpleHtmlTag_Directive,
       wxTTSSimpleHtmlTag_Entity };

class wxTTSSimpleHtmlTag
{
public:
    wxTTSSimpleHtmlTag(const wxString& tagName, int tagType);
    ~wxTTSSimpleHtmlTag();

//// Operations
    void ClearAttributes();
    wxTTSSimpleHtmlAttribute* FindAttribute(const wxString& name) const ;
    void AppendAttribute(const wxString& name, const wxString& value);
    void ClearChildren();
    void AppendTag(wxTTSSimpleHtmlTag* tag);
    // Write this tag
    void Write(wxOutputStream& stream);

    // Gets the text from this tag and its descendants
    wxString GetTagText();

//// Accessors
    const wxString& GetName() const { return m_name; }
    void SetName(const wxString& name) { m_name = name; }

    int GetType() const { return m_type; }
    void SetType(int t) { m_type = t; }

    // If type is wxTTSSimpleHtmlTag_Text, m_text will contain some text.
    const wxString& GetText() const { return m_text; }
    void SetText(const wxString& text) { m_text = text; }

    wxTTSSimpleHtmlAttribute* GetFirstAttribute() { return m_attributes; }
    void SetFirstAttribute(wxTTSSimpleHtmlAttribute* attr) { m_attributes = attr; }

    int GetAttributeCount() const ;
    wxTTSSimpleHtmlAttribute* GetAttribute(int i) const ;

    wxTTSSimpleHtmlTag* GetChildren() const { return m_children; }
    void SetChildren(wxTTSSimpleHtmlTag* children) { m_children = children; }

    wxTTSSimpleHtmlTag* GetParent() const { return m_parent; }
    void SetParent(wxTTSSimpleHtmlTag* parent) { m_parent = parent; }
    int GetChildCount() const;
    wxTTSSimpleHtmlTag*    GetChild(int i) const;
    wxTTSSimpleHtmlTag*    GetNext() const { return m_next; }

//// Convenience accessors & search functions
    bool NameIs(const wxString& name) { return (m_name.CmpNoCase(name) == 0); }
    bool HasAttribute(const wxString& name, const wxString& value) const;
    bool HasAttribute(const wxString& name) const;
    bool GetAttributeValue(wxString& value, const wxString& attrName);

    // Search forward from this tag until we find a tag with this name & optionally attribute 
    wxTTSSimpleHtmlTag* FindTag(const wxString& tagName, const wxString& attrName = wxEmptyString);

    // Gather the text until we hit the given close tag
    bool FindTextUntilTagClose(wxString& text, const wxString& tagName);

private:
    wxString                m_name;
    int                     m_type;
    wxString                m_text;
    wxTTSSimpleHtmlAttribute*  m_attributes;

    // List of children
    wxTTSSimpleHtmlTag*        m_children;
    wxTTSSimpleHtmlTag*        m_next; // Next sibling
    wxTTSSimpleHtmlTag*        m_parent;
};

/*
 * wxTTSSimpleHtmlParser
 * Simple HTML parser, for such tasks as scanning HTML for keywords, contents, etc.
 */

class wxTTSSimpleHtmlParser : public wxObject
{
    
public:
    wxTTSSimpleHtmlParser();
    ~wxTTSSimpleHtmlParser();

//// Operations
    bool ParseFile(const wxString& filename, const wxString& encoding = wxEmptyString);
    bool ParseString(const wxString& str);
    void Clear();
    // Write this file
    void Write(wxOutputStream& stream);
    bool WriteFile(wxString& filename);

//// Helpers

    // Main recursive parsing function
    bool ParseHtml(wxTTSSimpleHtmlTag* parent);

    wxTTSSimpleHtmlTag* ParseTagHeader();
    wxTTSSimpleHtmlTag* ParseTagClose();
    bool ParseAttributes(wxTTSSimpleHtmlTag* tag);
    wxTTSSimpleHtmlTag* ParseDirective(); // e.g. <!DOCTYPE ....>
    bool ParseComment(); // Throw away comments
    // Plain text, up until an angled bracket
    bool ParseText(wxString& text);

    bool EatWhitespace(); // Throw away whitespace
    bool EatWhitespace(int& pos); // Throw away whitespace: using 'pos'
    bool ReadString(wxString& str, bool eatIt = FALSE);
    bool ReadWord(wxString& str, bool eatIt = FALSE);
    bool ReadNumber(wxString& str, bool eatIt = FALSE);
    // Could be number, string, whatever, but read up until whitespace.
    bool ReadLiteral(wxString& str, bool eatIt = FALSE);

    bool IsDirective();
    bool IsComment();
    bool IsString();
    bool IsWord();
    bool IsTagClose();
    bool IsTagStartBracket(int ch);
    bool IsTagEndBracket(int ch);
    bool IsWhitespace(int ch);
    bool IsAlpha(int ch);
    bool IsWordChar(int ch);
    bool IsNumeric(int ch);

    // Matches this string (case insensitive)
    bool Matches(const wxString& tok, bool eatIt = FALSE) ;
    bool Eof() const { return (m_pos >= m_length); }
    bool Eof(int pos) const { return (pos >= m_length); }

    void SetPosition(int pos) { m_pos = pos; }


//// Accessors
    wxTTSSimpleHtmlTag* GetTopLevelTag() const { return m_topLevel; }

    // Safe way of getting a character
    int GetChar(size_t i) const;
    
private:

    wxTTSSimpleHtmlTag*    m_topLevel;
    int                 m_pos;    // Position in string
    int                 m_length; // Length of string
    wxString            m_text;   // The actual text

};

#endif
    // _TTS_HTMLPARSER_H_

